/// Macro for initializing N-API modules
#[macro_export]
macro_rules! MODULE_INIT {
	(onLoad,$NAME:tt) => {
		$crate::MODULE_INIT!(onLoad, $NAME, $crate::module_register_noop);
	};
	(onLoad,$NAME:tt,$REG:path) => {
		$crate::MODULE_INIT!(onLoad, $NAME, $REG, $crate::module_init_noop);
	};
	(onLoad,$NAME:tt,$REG:path,$MOD:path) => {
		unsafe extern "C" fn napi_onLoad() {
			unsafe extern "C" fn register(env: ::napih::sys::napi_env, exports: ::napih::sys::napi_value) -> ::napih::sys::napi_value {
				let env = $crate::Env::from(env);
				let exports = $crate::Value::new(env, exports);
				let exports = $REG(env, &exports);
				let exports = $crate::module_register_linkme(env, &exports);
				exports.raw()
			}
			let mut module = $crate::Module::default();
			$MOD(module).name_cstr($NAME).func(Some(register)).regist();
		}
	};
	($NAME:tt) => {
		$crate::MODULE_INIT!($NAME, $crate::module_register_noop);
	};
	($NAME:tt,$REG:path) => {
		$crate::MODULE_INIT!($NAME, $REG, $crate::module_init_noop);
	};
	($NAME:tt,$REG:path,$MOD:path) => {
		#[used]
		#[cfg_attr(target_env = "ohos", link_section = ".init_array")]
		// #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func,mod_init_funcs")]
		// #[cfg_attr(windows, link_section = ".CRT$XCU")]
		static NAPIH_INIT: unsafe extern "C" fn() = napih_module_init_v0;
		unsafe extern "C" fn napih_module_init_v0() {
			unsafe extern "C" fn register(env: ::napih::sys::napi_env, exports: ::napih::sys::napi_value) -> ::napih::sys::napi_value {
				let env = $crate::Env::from(env);
				let exports = $crate::Value::new(env, exports);
				let exports = $REG(env, &exports);
				let exports = $crate::module_register_linkme(env, &exports);
				exports.raw()
			}
			let mut module = $crate::Module::default();
			$MOD(module).name_cstr($NAME).func(Some(register)).regist();
		}
	};
}

/// Macro for registering JavaScript classes
#[macro_export]
macro_rules! REG_CLASS {
	(ext,$NAME:tt,$SELF:tt) => {
		$crate::REG_CLASS!(ext, REGIST, $NAME, $SELF);
	};
	(ext,$SNAME:tt,$NAME:tt,$SELF:tt) => {
		const _: () = {
			#[$crate::linkme::distributed_slice($crate::CLASSES)]
			#[linkme(crate=$crate::linkme)]
			#[allow(non_upper_case_globals)]
			static $SNAME: $crate::RegClass = $crate::RegClass {
				id: ::core::any::TypeId::of::<$SELF>(),
				name: $NAME,
				func: Some(<$SELF as ::napih::NapiExternal>::__napih_constructor),
				interface: true,
			};
		};
	};
	($NAME:tt,$SELF:tt) => {
		$crate::REG_CLASS!(REGIST, $NAME, $SELF);
	};
	($SNAME:tt,$NAME:tt,$SELF:tt) => {
		const _: () = {
			#[$crate::linkme::distributed_slice($crate::CLASSES)]
			#[linkme(crate=$crate::linkme)]
			#[allow(non_upper_case_globals)]
			static $SNAME: $crate::RegClass = $crate::RegClass {
				id: ::core::any::TypeId::of::<$SELF>(),
				name: $NAME,
				func: Some(<$SELF as ::napih::NapiClass>::__napih_constructor),
				interface: false,
			};
		};
	};
}

/// Macro for registering JavaScript functions and methods
#[macro_export]
macro_rules! REG_FUNCTION {
	(constructor,$SELF:tt,$NAME:tt,$FUNC:ident) => {
		$crate::REG_FUNCTION!(constructor, $SELF, REGIST, $NAME, $FUNC);
	};
	(constructor,$SELF:tt,$SNAME:tt,$NAME:tt,$FUNC:ident) => {
		#[$crate::linkme::distributed_slice($crate::METHODS)]
		#[linkme(crate=$crate::linkme)]
		#[allow(non_upper_case_globals)]
		static $SNAME: $crate::RegMethod = $crate::RegMethod::Constructor {
			id: ::core::any::TypeId::of::<$SELF>(),
			func: Some($FUNC),
		};
	};
	(method,$SELF:tt,$NAME:tt,$FUNC:ident) => {
		$crate::REG_FUNCTION!(method, $SELF, REGIST, $NAME, $FUNC);
	};
	(method,$SELF:tt,$SNAME:tt,$NAME:tt,$FUNC:ident) => {
		#[$crate::linkme::distributed_slice($crate::METHODS)]
		#[linkme(crate=$crate::linkme)]
		#[allow(non_upper_case_globals)]
		static $SNAME: $crate::RegMethod = $crate::RegMethod::Method {
			id: ::core::any::TypeId::of::<$SELF>(),
			func: Some($FUNC),
			name: $NAME,
		};
	};
	(static,$SELF:tt,$NAME:tt,$FUNC:ident) => {
		$crate::REG_FUNCTION!(static, $SELF, REGIST, $NAME, $FUNC);
	};
	(static,$SELF:tt,$SNAME:tt,$NAME:tt,$FUNC:ident) => {
		#[$crate::linkme::distributed_slice($crate::METHODS)]
		#[linkme(crate=$crate::linkme)]
		#[allow(non_upper_case_globals)]
		static $SNAME: $crate::RegMethod = $crate::RegMethod::Static {
			id: ::core::any::TypeId::of::<$SELF>(),
			func: Some($FUNC),
			name: $NAME,
		};
	};
	(getter,$SELF:tt,$NAME:tt,$FUNC:ident) => {
		$crate::REG_FUNCTION!(getter, $SELF, REGIST, $NAME, $FUNC);
	};
	(getter,$SELF:tt,$SNAME:tt,$NAME:tt,$FUNC:ident) => {
		#[$crate::linkme::distributed_slice($crate::METHODS)]
		#[linkme(crate=$crate::linkme)]
		#[allow(non_upper_case_globals)]
		static $SNAME: $crate::RegMethod = $crate::RegMethod::Getter {
			id: ::core::any::TypeId::of::<$SELF>(),
			func: Some($FUNC),
			name: $NAME,
		};
	};
	(setter,$SELF:tt,$NAME:tt,$FUNC:ident) => {
		$crate::REG_FUNCTION!(setter, $SELF, REGIST, $NAME, $FUNC);
	};
	(setter,$SELF:tt,$SNAME:tt,$NAME:tt,$FUNC:ident) => {
		#[$crate::linkme::distributed_slice($crate::METHODS)]
		#[linkme(crate=$crate::linkme)]
		#[allow(non_upper_case_globals)]
		static $SNAME: $crate::RegMethod = $crate::RegMethod::Setter {
			id: ::core::any::TypeId::of::<$SELF>(),
			func: Some($FUNC),
			name: $NAME,
		};
	};
	($NAME:tt,$FUNC:ident) => {
		$crate::REG_FUNCTION!(REGIST, $NAME, $FUNC);
	};
	($SNAME:tt,$NAME:tt,$FUNC:ident) => {
		#[$crate::linkme::distributed_slice($crate::FUNCTIONS)]
		#[linkme(crate=$crate::linkme)]
		#[allow(non_upper_case_globals)]
		static $SNAME: $crate::RegFuction = $crate::RegFuction {
			func: Some($FUNC),
			name: $NAME,
		};
	};
}

/// Macro for exporting functions to JavaScript
#[macro_export]
macro_rules! EXPORT_FUNC {
	(mut,$SELF:tt,$NAME:tt,$FUNC:ident($($A:ident:$T:ty),*)) => {
		const _: () = {
			extern "C" fn _cb_(env: ::napih::sys::napi_env, info: ::napih::sys::napi_callback_info) -> ::napih::sys::napi_value {
				$crate::REG_FUNCTION!(method,$SELF,$NAME, _cb_);

				let env = $crate::Env::from(env);
				let Some((this,_, $($A,)*)): Option<($crate::Value,*mut core::ffi::c_void, $($T,)*)> = $crate::ArgsExtracter::new(env, info).try_into().throw(env) else {
					return core::ptr::null_mut();
				};
				// TODO instance of
				let Some(this) = unsafe{this.mut_wrap::<$SELF>()}.throw(env) else {
					return core::ptr::null_mut();
				};
				let ret = $SELF::$FUNC(this,$($A,)*);

				let Some(ret) = ret.into_napi(env).throw(env) else {
					return core::ptr::null_mut();
				};
				ret.raw()
			}
		};
	};
	(ref,$SELF:tt,$NAME:tt,$FUNC:ident($($A:ident:$T:ty),*)) => {
		const _: () = {
			extern "C" fn _cb_(env: ::napih::sys::napi_env, info: ::napih::sys::napi_callback_info) -> ::napih::sys::napi_value {
				$crate::REG_FUNCTION!(method,$SELF,$NAME, _cb_);

				let env = $crate::Env::from(env);
				let Some((this,_, $($A,)*)): Option<($crate::Value,*mut core::ffi::c_void, $($T,)*)> = $crate::ArgsExtracter::new(env, info).try_into().throw(env) else {
					return core::ptr::null_mut();
				};
				// TODO instance of
				let Some(this) = unsafe{this.ref_wrap::<$SELF>()}.throw(env) else {
					return core::ptr::null_mut();
				};
				let ret = $SELF::$FUNC(this,$($A,)*);

				let Some(ret) = ret.into_napi(env).throw(env) else {
					return core::ptr::null_mut();
				};
				ret.raw()
			}
		};
	};
	(constructor,$SELF:tt,$NAME:tt,$FUNC:ident($THIS:ident:$THIS_TYPE:ty,$($A:ident:$T:ty),*)) => {
		const _: () = {
			extern "C" fn _cb_(env: ::napih::sys::napi_env, info: ::napih::sys::napi_callback_info) -> ::napih::sys::napi_value {
				$crate::REG_FUNCTION!(constructor,$SELF,$NAME, _cb_);

				let env = $crate::Env::from(env);
				let Some((this,_, $($A,)*)): Option<($crate::Value,*mut core::ffi::c_void, $($T,)*)> = $crate::ArgsExtracter::new(env, info).try_into().throw(env) else {
					return core::ptr::null_mut();
				};
				// TODO instance of
				let ret = $SELF::$FUNC(this,$($A,)*);
				this.wrap(ret).throw(env);
				this.raw()
			}
		};
	};
	(static,$SELF:tt,$NAME:tt,$FUNC:ident($($A:ident:$T:ty),*)) => {
		const _: () = {
			extern "C" fn _cb_(env: ::napih::sys::napi_env, info: ::napih::sys::napi_callback_info) -> ::napih::sys::napi_value {
				$crate::REG_FUNCTION!(static,$SELF,$NAME, _cb_);

				let env = $crate::Env::from(env);
				let Some((_,_, $($A,)*)): Option<($crate::Value,*mut core::ffi::c_void, $($T,)*)> = $crate::ArgsExtracter::new(env, info).try_into().throw(env) else {
					return core::ptr::null_mut();
				};
				// TODO instance of
				let ret = $SELF::$FUNC($($A,)*);

				let Some(ret) = ret.into_napi(env).throw(env) else {
					return core::ptr::null_mut();
				};
				ret.raw()
			}
		};
	};
	($NAME:tt,$FUNC:ident($($A:ident:$T:ty),*)) => {
		const _: () = {
			extern "C" fn _cb_(env: ::napih::sys::napi_env, info: ::napih::sys::napi_callback_info) -> ::napih::sys::napi_value {
				$crate::REG_FUNCTION!($NAME, _cb_);

				let env = $crate::Env::from(env);
				let Some((_,_, $($A,)*)): Option<($crate::Value,*mut core::ffi::c_void,$($T,)*)> = $crate::ArgsExtracter::new(env, info).try_into().throw(env) else {
					return core::ptr::null_mut();
				};

				let ret = $FUNC($($A,)*);

				let Some(ret) = ret.into_napi(env).throw(env) else {
					return core::ptr::null_mut();
				};
				ret.raw()
			}
		};
	};
}

/// Macro for registering constants
#[macro_export]
macro_rules! REG_CONST {
	(field,$SELF:tt,$NAME:tt,$FUNC:path) => {
		$crate::REG_CONST!(field, $SELF, REGIST, $NAME, $FUNC);
	};
	(field,$SELF:tt,$SNAME:tt,$NAME:tt,$FUNC:path) => {
		#[$crate::linkme::distributed_slice($crate::METHODS)]
		#[linkme(crate=$crate::linkme)]
		#[allow(non_upper_case_globals)]
		static $SNAME: $crate::RegMethod = $crate::RegMethod::Value {
			id: ::core::any::TypeId::of::<$SELF>(),
			func: $FUNC,
			name: $NAME,
		};
	};
	($NAME:tt,$FUNC:path) => {
		$crate::REG_CONST!(REGIST, $NAME, $FUNC);
	};
	($SNAME:tt,$NAME:tt,$FUNC:path) => {
		#[$crate::linkme::distributed_slice($crate::CONSTS)]
		#[linkme(crate=$crate::linkme)]
		#[allow(non_upper_case_globals)]
		static $SNAME: $crate::RegConst = $crate::RegConst {
			func: $FUNC,
			name: $NAME,
		};
	};
}

/// Macro for exporting constants to JavaScript
#[macro_export]
macro_rules! EXPORT_CONST {
	(field_fn,$SELF:tt,$NAME:tt,$FUNC:ident) => {
		const _: () = {
			$crate::REG_CONST!(field, $SELF, $NAME, $SELF::$FUNC);
		};
	};
	(field,$SELF:tt,$NAME:tt,$VALUE:tt) => {
		const _: () = {
			fn _cb_(env: &$crate::Env) -> $crate::Result<'_, $crate::Value<'_>> {
				$crate::REG_CONST!(field, $SELF, $NAME, _cb_);
				$SELF::$VALUE.into_napi(env)
			}
		};
	};
	(func,$NAME:tt,$FUNC:ident) => {
		const _: () = {
			$crate::REG_CONST!($NAME, $FUNC);
		};
	};
	($NAME:tt,$VALUE:tt) => {
		const _: () = {
			fn _cb_(env: &$crate::Env) -> $crate::Result<'_, $crate::Value<'_>> {
				$crate::REG_CONST!($NAME, _cb_);
				$VALUE.into_napi(env)
			}
		};
	};
}

/// Macro for registering TypeScript types
#[macro_export]
macro_rules! REG_TYPE {
	($NAME:literal,$TARGET:literal) => {
		const _: () = {
			#[$crate::linkme::distributed_slice($crate::TYPES)]
			#[linkme(crate=$crate::linkme)]
			#[allow(non_upper_case_globals)]
			static REGIST: $crate::RegType = $crate::RegType {
				name: $NAME,
				target: $TARGET,
			};
		};
	};
}

/// Macro for registering TypeScript declarations
#[macro_export]
macro_rules! REG_DTS {
	($NAME:literal,$TY:ident,$FUNC:ident) => {
		#[$crate::linkme::distributed_slice($crate::DTS)]
		#[linkme(crate=$crate::linkme)]
		#[allow(non_upper_case_globals)]
		static REGIST: $crate::RegDTS = $crate::RegDTS {
			name: $NAME,
			func: $FUNC,
			owner: None,
			ty: ::napih::DtsTy::$TY,
		};
	};
	($NAME:literal,$TY:ident,$FUNC:ident,$OWNER:path) => {
		#[$crate::linkme::distributed_slice($crate::DTS)]
		#[linkme(crate=$crate::linkme)]
		#[allow(non_upper_case_globals)]
		static REGIST: $crate::RegDTS = $crate::RegDTS {
			name: $NAME,
			func: $FUNC,
			owner: Some(::core::any::TypeId::of::<$OWNER>()),
			ty: ::napih::DtsTy::$TY,
		};
	};
}
